;
; SPELL version 2.0		December 22, 1982
;
;	written by Michael C. Adler
;
;    History of SPELL's dictionary:
;	The first SPELL using this dictionary was probably written
;	by Ralph Gorin at Stanford.  It was transported to MIT by
;	Wayne Mattson.  Both the program at MIT and the dictionary
;	were most recently revised by William Ackerman.
;
;		Thanks to all for the effort spent desigining
;		the dictionary!
;						-Michael
;
; 
	.Z80
	TITLE	SPELL
;	JMP	START    <--Include this line if assembled under anything
;			    but M80.

	DB	'(C) 1982 Michael C. Adler. '
	DB	'This program has been released into the public domain '
	DB	'by the author.  It may neither be sold for profit nor '
	DB	'included in a sold software package without permission '
	DB	'of the author.'
;
;	SPELL will compare words in a document to a dictionary
; and mark any words that are not found.  It is intended for use
; with WordStar's ^QL (Find Misspelling) command.  Words are marked
; with a preceding null if they are not found.
;
;	WARNING:  SPELL requires a Z80 processor.
;
;	MODIFICATION HISTORY:
;
;	2.0 - December 22, 1982  Michael Adler
;	      -Bug fix:  modified to rename files correctly at end of
;	       run if output drive is different from input drive.
;	      -Bug fix:  modified to recognize hyphenated words in indented
;	       text.
;	      -Enhanced to allow user specified dictionaries on command
;	       line.
;	1.3 - October 10, 1982  Michael Adler
;	      -Bug fix:  crashed if number of unique words was multiple
;	       of 256.
;	1.2 - October 8, 1982  Michael Adler
;	      -Modified MEMWRD routine to store words backwards in
;	       memory starting just below the BDOS.  Thus, maximum
;	       memory is utilized since pointers to words and the words
;	       grow toward each other.
;	      -FILE.UDC is now deleted if it is empty.
;	      -Added messages displaying number of words, number of
;	       unique words, and number of words not found.
;	1.1 - August 28, 1982  Michael Adler
;	      -Crashed if file.ADD had a zero length word.  Fixed.
;	      -Fixed bug in AUXDIC:  stopped reading from .ADD file
;	       when 0 encountered.
;	      -Fixed compatability with WS R(un program).  WS initializes
;	       byte at 6CH with default drive instead of 0.  Caused output
;	       to be directed to wrong drive sometimes.
;	      -Set SPELL to ignore lines starting with . because WS can't
;	       handle misspelling markers in dot command lines.
;	      -SPELL did not find hyphenated words if LF had parity bit
;	       set.  Fixed.
;
;	1.0 - August 16, 1982  Michael Adler
;	      -Released
;


	INCLUDE	SPELL0.MAC

ERRCHR	EQU	0			;THIS IS THE CHARACTER THAT MARKS
					;UNFOUND WORDS.  FOR WordStar, IT
					;MUST BE 0.  IF YOU ARE USING ANOTHER
					;EDITOR, SET ERRCHR TO THE ASCII
					;EQUIVALENT OF THE CHARACTER THAT
					;SHOULD MARK MISSPELLED WORDS.


BOOT	EQU	0000H
BDOS	EQU	BOOT+0005H
IFCB	EQU	BOOT+005CH
TPA	EQU	BOOT+0100H
CONOUT	EQU	2
STROUT	EQU	9
OPEN	EQU	15
CLOSE	EQU	16
DELETE	EQU	19
READ	EQU	20
WRITE	EQU	21
MAKE	EQU	22
RENAME	EQU	23
CURDSK	EQU	25
SETDMA	EQU	26			;SET DMA ADDRESS
RANREA	EQU	33			;READ RANDOM

LF	EQU	10
CR	EQU	13
EOF	EQU	01AH			;END OF FILE CHARACTER
;
;	The following are bit flags for the dictionary
;
ZFLAG	EQU	1
YFLAG	EQU	2
RFLAG	EQU	4
GFLAG	EQU	8
DFLAG	EQU	16
MFLAG	EQU	32
SFLAG	EQU	64
HFLAG	EQU	256
VFLAG	EQU	512
JFLAG	EQU	1024
XFLAG	EQU	2048
TFLAG	EQU	4096
NFLAG	EQU	8192
PFLAG	EQU	16384

;
;	Macros
;
JISIT	MACRO	CHAR, JMPLOC		;JUMP TO JMPLOC IF (HL) .EQ. CHAR
	LD	A,CHAR
	CP	(HL)
	JP	Z,JMPLOC
	ENDM

QISIT	MACRO	CHAR, JMPLOC		;QUICK FORM OF JISIT.  CHARACTER
					;TO COMPARE IS ALREADY IN A
	CP	CHAR
	JP	Z,JMPLOC
	ENDM

ISIT	MACRO	CHAR			;JUMP TO WRDNOT IF (HL) .NE. CHAR
	LD	A,CHAR
	CP	(HL)
	JP	NZ,WRDNOT
	ENDM


START:	LD	SP,STACK		;CREATE A STACK
	LD	A,0C9H			;RETURN INSTRUCTION
	LD	(TPA),A			;DON'T ALLOW SECOND RUN OF PROGRAM
	LD	DE,WELCOM		;PRINT SIGNON MESSAGE
	LD	C,STROUT
	CALL	BDOS
	JR	START0
WELCOM:	DB	'SPELL V2.0 -- December 22, 1982',CR,LF
	DB	'(C) 1982 Michael C. Adler',CR,LF,'$'
	
START0:	LD	A,(BOOT+6CH)		;GET OUTPUT DRIVE
	PUSH	PSW			;SAVE IT
	LD	C,OPEN			;OPEN THE DICTIONARY FILE
	LD	DE,DICFCB
	CALL	BDOS
	CP	0FFH			;FOUND?
	JP	NZ,SETUP		;JUMP IF FOUND
	LD	A,1			;TRY DRIVE A IF NOT ON DEFAULT DRIVE
	LD	(DICFCB),A
	LD	C,OPEN
	LD	DE,DICFCB
	CALL	BDOS
	CP	0FFH			;FOUND?
	JP	NZ,SETUP
	LD	C,STROUT		;OUTPUT:  "CAN'T FIND DICTIONARY..."
	LD	DE,DICERR
	CALL	BDOS
	CALL	0
DICERR:	DB	'Can''t find dictionary file DICT.DIC',CR,LF,'$'

SETUP:	LD	DE,OFCB			;COPY INPUT FILENAME TO OUTPUT
	LD	HL,IFCB
	LD	BC,9
	LDIR
	LD	DE,P2FCB		;COPY INPUT FILENAME TO PASS 2 FCB
	LD	HL,IFCB			; (THIS FCB IS FOR INPUT ON PASS 2)
	LD	BC,12
	LDIR
	LD	DE,FILDIC		;COPY INPUT FILENAME TO FILE.DIC FCB
	LD	HL,IFCB
	LD	BC,9
	LDIR
	LD	DE,FILADD		;COPY INPUT FILENAME TO FILE.ADD FCB
	LD	HL,IFCB
	LD	BC,9
	LDIR
	LD	DE,FILD$$		;COPY INPUT FILENAME TO FILE.D$$ FCB
	LD	HL,IFCB
	LD	BC,9
	LDIR
	POP	PSW			;GET OUTPUT DRIVE, IF SPECIFIED (FROM
					;6CH)
	CP	0
	JR	Z,NODRV
	LD	HL,BOOT+81H		;SEARCH FOR DRIVE SPECIFICATION IN
					;COMMAND LINE
	LD	A,(BOOT+80H)		;NUMBER OF CHARACTERS IN COMMAND LINE
	LD	B,0
	LD	C,A			;COUNTER
	ADD	HL,BC			;HL POINTS TO LAST CHARACTER
	DEC	BC			;DON'T TEST FIRST BYTE OF INPUT
	LD	A,':'			;SEARCHING FOR ":"
	CPDR
	JR	NZ,NODRV		;IF NO DRIVE SPECIFIED, THEN USE
					;DEFAULT
	LD	A,(HL)			;GET DRIVE NAME
	AND	5FH			;MAKE UPPER CASE
	SUB	40H			;MAKE A=1
	LD	(OFCB),A		;MAKE OUTPUT FILES ON OUTPUT DRIVE
	LD	(FILD$$),A
NODRV:	LD	C,OPEN			;OPEN THE INPUT FILE
	LD	DE,IFCB
	CALL	BDOS
	LD	C,OPEN			;WITH PASS 1 AND PASS 2 FCB'S
	LD	DE,P2FCB
	CALL	BDOS
	CP	0FFH
	JP	NZ,SETUP0		;JUMP IF FOUND
	LD	C,STROUT		;OUTPUT:  "CAN'T FIND INPUT FILE"
	LD	DE,NOINPT
	CALL	BDOS
	CALL	BOOT
NOINPT:	DB	'Can''t find input file',CR,LF,'$'

SETUP0:	LD	C,DELETE		;DELETE OUTPUT FILE (FILE.$$$)
	LD	DE,OFCB
	CALL	BDOS
	LD	C,MAKE			;MAKE A NEW OUTPUT FILE
	LD	DE,OFCB
	CALL	BDOS
	CP	0FFH
	JP	NZ,SETUP1		;JUMP IF SUCCESSFUL
	LD	C,STROUT		;OUTPUT:  "DIRECTORY FULL"
	LD	DE,NODIR
	CALL	BDOS
	CALL	BOOT
NODIR:	DB	'Directory full',CR,LF,'$'

SETUP1:	CALL	CTRLZ			;FILL OUTPUT BUFFER WITH EOF
	LD	A,EOF			;MARK END OF OUTPUT BUFFER
	LD	(OBUFF+512),A
	LD	A,0			;MARK END OF DICTIONARY BUFFER
	LD	(DICBUF+256),A
	LD	(DICBUF+257),A
	LD	A,(PUTCHR)		;GET THE NORMAL FIRST INSTRUCTION
					;AT PUTCHR
	LD	(SAVPUT),A		;STORE IT FOR LATER
	LD	A,0C9H			;RETURN INSTRUCTION
	LD	(PUTCHR),A		;DISABLE OUTPUT
	CALL	AUXDIC			;LOAD "SPELL.DIC", FILE.DIC AND
					; FILE.ADD
	LD	HL,IFCB			;INPUT FILE FCB
	LD	(FILPAT),HL		;PATCH GETWRD: TO READ FROM IT


;
; SRTFIL -- Sort the file in memory, alphabetically.  Duplicate words are
;	discarded.  This will save a lot of time during dictionary lookups
;	since the words will have to be located only once.

SRTFIL:	CALL	GETWRD			;GET A WORD INTO SRCWRD:
	JP	Z,CHECK			;IF EOF THEN START CHECKING
	LD	HL,(TOTWRD)		;KEEP TRACK OF TOTAL # OF WORDS
	INC	HL
	LD	(TOTWRD),HL
	CALL	CREWRD			;CREATE WORD FROM SRCWRD:
	LD	A,0			;IS IT AT LEAST 2 CHARS LONG?
	CP	C
	JR	Z,SRTFIL		;FORGET IT IF NOT
	LD	A,41			;IF LONGER THAN 40, ALSO FORGET IT
	CP	C
	JR	Z,SRTFIL
	CALL	MEMWRD			;PUT WORD IN MEMORY
	AND	A			;FULL?
	JR	Z,SRTFIL		;GET ANOTHER WORD IF NOT
	LD	HL,(INPTR)		;GET OLD INPUT BUFFER POINTER
	LD	(OLDPTR),HL		;SAVE IT
	LD	A,1			;MARK PASS 1 INCOMPLETE
	LD	(STOPED),A
	LD	(MLTPAS),A		;RECORD PERMANENTLY THAT >1 PASS
	LD	C,36			;GET RECORD NUMBER OF INPUT FILE
	LD	DE,IFCB
	CALL	BDOS


;
; CHECK -- Compare the entries in the sorted table to the dictionary.
;	Words that are found are marked.
;
CHECK:	LD	A,(STOPED)		;IS READ OF SOURCE COMPLETE?
	CP	0
	JR	NZ,CHEC01		;IF NOT, NO MESSAGE
	LD	DE,TOTMSG		;OUTPUT: "TOTAL # OF WORDS"
	LD	C,STROUT
	CALL	BDOS
	LD	HL,(TOTWRD)		;PRINT NUMBER
	CALL	DECOUT
CHEC01:	LD	HL,(SRTTOP)		;TOP ENTRY IN POINTER TABLE
	LD	DE,(SRTBOT)		;BOTTOM
	XOR	A			;CLEAR CARRY
	DEC	HL			;POINT TO TRUE END, NOT DUMMY RECORD
	DEC	HL
	SBC	HL,DE			;HL=NUMBER OF WORDS TIMES 2
	JP	Z,MAIN			;IF EMPTY, WRITE OUT FILE
	RRC	L			;DIVIDE HL BY 2
	LD	A,L
	AND	07FH
	LD	L,A
	LD	A,0
	RRC	H
	ADC	A,0
	RRC	A
	OR	L
	LD	L,A
	LD	A,H
	AND	07FH
	LD	H,A
	LD	B,H			;PUT COUNTER IN BC
	LD	C,L
	INC	B			;MAKE B WORD WITH DEC B AND JNZ
					;COMBINATION
	LD	A,0
	CP	C			;IF C 0?
	JR	NZ,CHEC02
	DEC	B			;IF C=0 THEN READJUST B BECAUSE
					;IT WOULD LOOP 256 TOO MANY TIMES
CHEC02:	LD	HL,(SRTBOT)		;GET ADDRESS OF FIRST ENTRY
	INC	HL			;BYPASS DUMMY ENTRY AT BEGINNING
	INC	HL
CHECK0:	LD	E,(HL)			;GET ADDRESS OF WORD IN MEMORY
	INC	HL
	LD	D,(HL)
	INC	HL
	PUSH	BC
	PUSH	HL
	LD	HL,WORD
	LD	C,0FEH			;CHARACTER COUNTER
					;AT THE END, C=CHARS-1.  START OF WITH
					;-2 SO THAT 0 AND 1 CHARACTER NOT
					;COUNTED
CHECK1:	LD	A,(DE)			;GET A CHARACTER
	LD	(HL),A			;PUT IT IN WORD:
	INC	C
	DEC	DE
	INC	HL
	CP	0			;END OF WORD?
	JR	NZ,CHECK1		;LOOP UNTIL END
	LD	A,C
	LD	(LASTC),A		;NUMBER OF CHARACTERS
	LD	HL,(UNQWRD)		;RECORD # OF UNIQUE WORDS
	INC	HL
	LD	(UNQWRD),HL
	CALL	WRDTST			;SEARCH DICTIONARY FOR WORD
	CP	1			;FOUND?
	JR	NZ,CHECK2		;LEAVE UNMARKED IF NOT FOUND
	INC	DE			;POINT TO END MARKER OF WORD
	LD	A,080H
	LD	(DE),A			;MARK WORD AS FOUND
	JR	CHECK3
CHECK2:	LD	HL,(MISWRD)		;INCREMENT MISSED WORDS COUNTER
	INC	HL
	LD	(MISWRD),HL
CHECK3:	POP	HL
	POP	BC
	DEC	C
	JP	NZ,CHECK0		;LOOP
	DEC	B
	JP	NZ,CHECK0		;16 BIT LOOP INDEX
	LD	HL,P2FCB		;SET TO INPUT FROM PASS 2 FCB
	LD	(FILPAT),HL
	LD	HL,(P2OPTR)		;SET PASS 2 INPUT POINTER
	LD	(INPTR),HL
	LD	A,(SAVPUT)		;GET THE NORMAL FIRST INSTRUCTION
					;AT PUTCHR
	LD	(PUTCHR),A		;PATCH IT INTO THE WRITE ROUTINE
	LD	HL,P2BUF+512		;PATCH NEW BUFFER INTO GETCHR
	LD	(BUFPAT),HL
	LD	HL,P2BUF
	LD	(BUFPA0),HL
	LD	(BUFPA1),HL
	INC	HL
	LD	(BUFPA2),HL
	LD	A,(LASTCH)		;SAVE LAST CHARACTER READ
	LD	B,A
	LD	A,(OLSTCH)		;PUT OLD LASTCH BACK
	LD	(LASTCH),A
	LD	A,B
	LD	(OLSTCH),A		;SAVE CURRENT LASTCH
	JR	MAIN
TOTMSG:	DB	CR,LF,LF,'Total number of words in document:	$'

;
; MAIN -- Do a second pass through the input file.  Compare each word to
;	the memory buffer to determine whether the words were found.
;	If a word was not found, mark it with the ERRCHR.

MAIN:	CALL	GETWRD			;GET A WORD INTO SRCWRD:
	JP	Z,DONE			;IF EOF THEN STOP
	CALL	CREWRD			;CREATE WORD FROM SRCWRD:
	LD	A,0			;IS WORD AT LEAST 2 CHARS LONG?
	CP	C
	JR	Z,MAIN0			;ACCEPT IF NOT
	LD	A,41			;IF LONGER THAN 40, ALSO ACCEPT
	CP	C
	JR	Z,MAIN0
	CALL	MEMWRD			;SEARCH FOR WORD IN MEMORY
	LD	A,(DE)			;GET MARKER FOR WORD
	CP	080H			;WORD SPELLED CORRECTLY?
	JR	Z,MAIN0
	LD	A,ERRCHR		;IF NOT, MARK WORD
	CALL	PUTCHR
MAIN0:	LD	HL,SRCWRD		;OUTPUT THE ORIGINAL WORD
MAIN1:	LD	A,(HL)
	CP	0			;END OF WORD?
	JR	Z,MAIN2
	CALL	PUTCHR			;OUTPUT CHARACTER
	INC	HL			;POINT TO NEXT CHARACTER
	JR	MAIN1			;LOOP
MAIN2:	LD	A,(STOPED)		;WAS PASS 1 INCOMPLETE
	AND	A
	JR	Z,MAIN			;GET NEXT WORD IF NOT
	LD	DE,P2FCB		;COMPUTE CURRENT RECORD NUMBER
	LD	C,36
	CALL	BDOS
	LD	DE,(P2FCB+21H)
	LD	HL,(IFCB+21H)		;COMPARE STOPPED RECORD NUMBER TO
					;CURRENT
	XOR	A			;CLEAR CARRY
	SBC	HL,DE
	JP	NZ,MAIN			;GET NEXT WORD IF NOT THE SAME
	LD	HL,(OLDPTR)		;GET POSITION IN RECORD
	LD	DE,P2BUF-INBUF		;OFFSET IT TO COMPARE WITH PASS 2 BUFF
	ADD	HL,DE
	LD	DE,(INPTR)
	XOR	A
	SBC	HL,DE			;COMPARE STOPPED POS TO CURRENT
	JP	NZ,MAIN			;GET NEXT WORD IF NOT THE SAME
	LD	A,0			;UNMARK PASS 1 INCOMPLETE
	LD	(STOPED),A
	LD	HL,(INPTR)		;SAVE PASS 2 INPUT POINTER
	LD	(P2OPTR),HL
	LD	HL,(OLDPTR)		;GET OLD POINTER TO BUFFER
	LD	(INPTR),HL		;RESET BUFFER POINTER
	LD	HL,IFCB			;PATCH GETCHR: ROUTINE TO READ FROM
					;PASS 1 FCB
	LD	(FILPAT),HL
	LD	HL,INBUF+512		;PATCH THE CORRECT BUFFER INTO GETCHR
	LD	(BUFPAT),HL
	LD	HL,INBUF
	LD	(BUFPA0),HL
	LD	(BUFPA1),HL
	INC	HL
	LD	(BUFPA2),HL
	LD	A,0C9H			;MAKE PUTCHR NOT WORK
	LD	(PUTCHR),A
	LD	HL,(SRTBOT)		;RESET MEMORY BUFFER POINTERS
	INC	HL
	INC	HL
	LD	(SRTTOP),HL
	LD	HL,(BDOS+1)		;GET BDOS VECTOR
	DEC	HL			;POINT TO FREE MEMORY
	LD	(FREE),HL		;POINTER TO FREE MEMORY
	LD	A,(LASTCH)		;SAVE LAST CHARACTER READ
	LD	B,A
	LD	A,(OLSTCH)		;PUT OLD LASTCH BACK
	LD	(LASTCH),A
	LD	A,B
	LD	(OLSTCH),A		;SAVE CURRENT LASTCH
	JP	SRTFIL			;FILL BUFFER AGAIN

;
; DONE -- Write out the rest of the output buffer and then close and rename
;	output files.

DONE:	LD	HL,OBUFF		;WRITE OUT REMAINING DATA IN BUFFER
	LD	DE,128
DONE1:	LD	A,EOF			;DONE?
	CP	(HL)
	JR	Z,DONE3
	PUSH	DE
	PUSH	HL
	LD	D,H			;SET UP DMA ADDRESS
	LD	E,L
	LD	C,SETDMA
	CALL	BDOS
	LD	DE,OFCB			;WRITE 128 BYTES TO FILE
	LD	C,WRITE
	CALL	BDOS
	POP	HL
	POP	DE
	CP	0			;ERROR?
	JR	NZ,DONE2		;DISK FULL --> BRANCH
	ADD	HL,DE			;POINT TO NEXT RECORD
	JR	DONE1
DONE2:	LD	C,STROUT		;DISK FULL
	LD	DE,DSKFUL		;DISK FULL MESSAGE (SEE PUTCHR)
	CALL	BDOS
	CALL	BOOT

DONE3:	LD	DE,OFCB			;CLOSE OUTPUT FILE
	LD	C,CLOSE
	CALL	BDOS
	LD	A,'B'
	LD	(OFCB+9),A
	LD	A,'A'
	LD	(OFCB+10),A
	LD	A,'K'
	LD	(OFCB+11),A
	LD	A,(OFCB)		;REMEMBER OUTPUT DRIVE NAME
	PUSH	PSW
	LD	A,(IFCB)		;GET THE DRIVE ON WHICH INPUT WAS DONE
	LD	(OFCB),A		;TRY TO DELETE .BAK ON IT ONLY
	LD	DE,OFCB
	LD	C,DELETE
	CALL	BDOS
	LD	DE,IFCB+16		;RENAME SOURCE TO SOURCE.BAK
	LD	HL,OFCB
	LD	BC,12			;COPY FILENAME FOR RENAME
	LDIR
	LD	DE,IFCB
	LD	C,RENAME
	CALL	BDOS
	LD	HL,IFCB+9		;RENAME .$$$ TO SOURCE
	LD	DE,IFCB+25
	LD	BC,3
	LDIR
	LD	A,'$'
	LD	(IFCB+9),A
	LD	(IFCB+10),A
	LD	(IFCB+11),A
	POP	PSW			;RESTORE OUTPUT DRIVE NAME
	LD	(IFCB),A		;AND PUT IT IN FCB
	LD	DE,IFCB
	LD	C,RENAME
	CALL	BDOS
					;NOW TAKE CARE OF USER DICTIONARIES
	LD	DE,FILADD		;DELETE FILE.ADD
	LD	C,DELETE
	CALL	BDOS
	LD	DE,FILDIC		;DELETE FILE.UDC
	LD	C,DELETE
	CALL	BDOS
	LD	A,(MLTPAS)		;WAS MORE THAN 1 PASS REQUIRED?
	CP	0
	JR	Z,DONE30		;NO MESSAGE IF NOT
	LD	DE,NOTOUT		;OUTPUT: NO TOTALS PRINTED
	LD	C,STROUT
	CALL	BDOS
	JR	DONE31
DONE30:	LD	DE,UNQMSG		;OUTPUT: # OF UNIQUE WORDS
	LD	C,STROUT
	CALL	BDOS
	LD	HL,(UNQWRD)		;PRINT # OF WORDS
	CALL	DECOUT
	LD	DE,MISMSG		;OUTPUT: # OF "MISSPELLED" WORDS
	LD	C,STROUT
	CALL	BDOS
	LD	HL,(MISWRD)		;PRINT #
	CALL	DECOUT
DONE31:	LD	HL,(FILUDC)		;DELETE FILE.D$$ IF IT HAS NO WORDS
	LD	A,0
	CP	H
	JR	NZ,DONE4		;RENAME IT TO FILE.UDC IF >0 WORDS
	CP	L
	JR	NZ,DONE4
	LD	DE,FILD$$		;DELETE IT
	LD	C,DELETE
	CALL	BDOS
	CALL	BOOT

DONE4:	LD	HL,FILDIC		;RENAME FILE.D$$ TO FILE.UDC
	LD	DE,FILD$$+16
	LD	BC,12
	LDIR
	LD	DE,FILD$$
	LD	C,RENAME
	CALL	BDOS
	CALL	BOOT
NOTOUT:	DB	CR,LF,'No subtotals available due to size of document.$'
UNQMSG:	DB	CR,LF,'	Number of unique words:		$'
MISMSG:	DB	CR,LF,'	Unique "misspelled" words:	$'

;
; GETWRD -- Read a word from an input file.  Words are any series of
;	alphabetic characters (with or without parity set) and
;	apostrophes.
;
;		Returns:  C <-- number of characters in word
;			  Z flag set if EOF

GETWRD:	CALL	GETCHR			;GET A CHARACTER
	CP	EOF			;END OF FILE?
	RET	Z
	CALL	LEGAL			;TEST IF LEGAL
	LD	(LASTCH),A		;SAVE LAST CHARACTER
	JR	Z,KILPER		;EXIT LOOP IF LEGAL
	CALL	PUTCHR			;SEND CHARACTER DIRECTLY OUT
	JR	GETWRD			;LOOP UNTIL LEGAL
KILPER:	LD	C,A
	AND	07FH			;MASK PARITY
	CP	'.'			;IS IT A DOT COMMAND?
	LD	A,C
	JR	NZ,GETWR0		;GET WORD IF NOT
	CALL	PUTCHR
KILPE0:	CALL	GETCHR			;ERADICATE THIS LINE
	CP	EOF			;END OF FILE?
	RET	Z
	CALL	PUTCHR			;OUTPUT IT
	LD	(LASTCH),A
	AND	07FH			;MASK PARITY
	CP	LF			;IS IT A LINE TERMINATOR?
	JR	NZ,KILPE0		;LOOP THROUGH WHOLE LINE
	JR	GETWRD			;GET A TEXT WORD NOW
GETWR0:	LD	C,1			;ZERO CHARACTER COUNTER
	LD	HL,SRCWRD+1		;INITIALIZE POINTER TO SRCWRD:
	LD	(SRCWRD),A		;STORE FIRST CHARACTER
GETWR1:	CALL	GETCHR
HYPTST:	CP	1FH			;SOFT HYPHEN?
	JP	Z,ISHYP
	CP	1EH			;UNUSED SOFT HYPHEN?
	JP	NZ,NOTHYP
ISHYP:	LD	(LASTCH),A		;UPDATE LAST CHARACTER POINTER
	LD	(HL),A			;STORE HYPHEN
	INC	HL
	INC	C
	CALL	GETCHR
ISHYP0:	CP	CR OR 80H		;SOFT RETURN AFTER HYPHEN?
	JR	NZ,HYPTST		;TEST FOR ANOTHER HYPHEN
	LD	(LASTCH),A		;UPDATE LAST CHARACTER POINTER
	LD	(HL),A			;STORE RETURN
	INC	HL
	INC	C
	CALL	GETCHR
	LD	B,A			;SAVE THE CHAR
	AND	07FH			;KILL PARITY BIT
	CP	LF			;SOFT LINE FEED?
	LD	A,B			;RESTORE CHAR
	JR	NZ,HYPTST		;TEST FOR ANOTHER HYPHEN
	LD	(LASTCH),A		;UPDATE LAST CHARACTER POINTER
	LD	(HL),A			;STORE LF
	INC	HL
	INC	C
	CALL	GETCHR
ISHYP1:	CP	' ' OR 80H		;ELIMINATE SOFT SPACES AT START OF LINE
	JR	NZ,ISHYP0		;TEST FOR REST OF WORD
	LD	(LASTCH),A		;UPDATE LAST CHARACTER POINTER
	LD	(HL),A			;STORE SPACE
	INC	HL
	INC	C
	CALL	GETCHR
	JR	ISHYP1			;TEST FOR MORE SPACES	
NOTHYP:	CALL	LEGAL
	JR	NZ,GETWR2		;EXIT LOOP WHEN NOT WORD CHARACTER
	LD	(LASTCH),A
	LD	(HL),A
	INC	HL
	INC	C
	JR	GETWR1
GETWR2:	LD	(HL),0			;MARK END OF WORD
	LD	HL,(INPTR)		;DECREMENT POINT FOR INPUT BUFFER
					;SO FIRST CHAR AFTER WORD IS KEPT
	DEC	HL
	LD	(INPTR),HL
	RET

;
; LEGAL -- Determines whether character is alphabetic, an apostrophe,
;	or period for a dot command.
;
;		Returns:  Z flag set <-- characters was one of the above
;			  Z flag clear <-- was not one of the above

LEGAL:	LD	B,A
	AND	05FH			;KILL PARITY AND LOWER CASE
	CP	'A'			;MUST BE GREATER THAN "A"
	JP	C,LEGAL0
	CP	'Z'+1			;GREATER THAN "Z"
	JP	NC,LEGAL0
	LD	A,B
	CP	A			;SET ZERO FLAG
	RET
LEGAL0:	LD	A,B
	AND	07FH			;KILL ONLY PARITY
	CP	27H			;"'"
	JR	Z,LEGAL1
	LD	A,(LASTCH)		;WAS LAST CHARACTER A LF?
	AND	7FH			;MASK PARITY
	CP	LF
	JR	NZ,LEGAL1
	LD	A,B
	CP	'.'			;ACCEPT PERIODS SO DOT COMMAND ACCEPTED
LEGAL1:	LD	A,B
	RET

;
; GETCHR -- Read a character from an input file into a buffer.  This routine
;	is used for a number of different purposes.  The file from which
;	to read is patched into FILPAT.  The buffer to which writes should
;	occur is patched into BUFPAT-BUFPA2.
;
;		Returns:  A  <-- next character from input file

GETCHR:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,(INPTR)		;POINTER FOR INPUT
	LD	DE,INBUF+512		;END OF INPUT BUFFER
BUFPAT	EQU	$-2			;PATCH FOR INPUT BUFFER
	XOR	A			;CLEAR CARRY
	PUSH	HL
	SBC	HL,DE			;AT END OF BUFFER?
	POP	HL
	JP	Z,GETCH0		;REFILL BUFFER
	LD	A,(HL)			;GET THE CHARACTER
	INC	HL			;INCREMENTED POINTER
	LD	(INPTR),HL
GETRET:	POP	HL
	POP	DE
	POP	BC
	RET
GETCH0:	LD	HL,INBUF
BUFPA0	EQU	$-2			;PATCH FOR INPUT BUFFER
	LD	DE,128
	LD	C,4
GETCH2:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	A,EOF			;MARK WITH EOF IN CASE NO RECORD
	LD	(HL),A
	LD	D,H			;SET UP DMA ADDRESS FOR READ
	LD	E,L
	LD	C,SETDMA
	CALL	BDOS			;SET DMA ADDRESS
	LD	DE,IFCB
FILPAT	EQU	$-2			;FCB PATCH ADDRESS
	LD	C,READ			;READ 128 BYTES OF INPUT FILE
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	CP	0			;SUCCESS?
	JR	NZ,GETCH3		;JUMP IF EOF
	ADD	HL,DE			;POINT TO NEXT RECORD ADDRESS
	DEC	C
	JP	NZ,GETCH2		;LOOP FOR 4 RECORDS
GETCH3:	LD	A,(INBUF)		;GET FIRST CHARACTER
BUFPA1	EQU	$-2			;ANOTHER BUFFER PATCH
	LD	(HL),EOF		;PUT EOF AT BEGINNING OF FIRST UNUSED
					;RECORD IN MEMORY
	LD	HL,INBUF+1
BUFPA2	EQU	$-2			;AND YET ANOTHER BUFFER PATCH
	LD	(INPTR),HL		;SET UP POINTER TO RECORDS
	JP	GETRET

;
; CREWRD -- Converts the word in SRCWRD to a word in WORD.  Parity bits
;	are cleared, lower case is converted to upper case, and apostrophes
;	that are not imbedded in words are discarded.

CREWRD:	LD	HL,WORD			;POINTER TO DESINATION BUFFER
	LD	DE,SRCWRD		;SOURCE BUFFER
	LD	C,0
CREWR0:	LD	A,(DE)			;GET A CHARACTER
	CP	0			;END OF WORD?
	JP	Z,CREWR2
	AND	07FH			;MASK PARITY
	CP	027H			;"'" CHARACTER?
	JR	Z,CREWR3
	AND	05FH			;MASK PARITY AND CONVERT TO UPPER CASE
	CP	'A'			;IS IT ONLY A HYPHEN?
	JR	C,CREWR1		;IF SO, SKIP IT
CREADD:	INC	C
	LD	(HL),A			;PUT CHARACTER IN WORD BUFFER
	LD	A,41			;MAXIMUM OF 40 CHARACTERS IN WORD
	CP	C
	RET	Z			;WORD TOO LONG.  ACCEPT IT
	INC	HL
CREWR1:	INC	DE
	JP	CREWR0
CREWR2:	LD	(HL),0			;MARK END OF WORD
	LD	HL,LASTC		;PUT NUMBER OF CHARACTERS IN LASTC
	DEC	C			;C=NUMBER OF CHARACTERS - 1
	JP	P,CREWS2		;JUMP IF C WASN'T ALREADY 0
	INC	C
CREWS2:	LD	(HL),C
	RET
CREWR3:	LD	A,0			;IF FIRST CHARACTER IN WORD, IGNORE
					;"'"
	CP	C
	JR	Z,CREWR1
	INC	DE
	LD	A,(DE)			;IF LAST CHAR IN WORD, IGNORE
	CP	0
	DEC	DE
	JR	Z,CREWR1
	LD	A,27H			;OTHERWISE, KEEP "'"
	JR	CREADD

;
; PUTCHR -- write a character to output file
;
;		Input:  A --> character to output

PUTCHR:	PUSH	PSW
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,(OPOSS)		;GET CURRENT POSITION IN OBUFF
	LD	(HL),A			;PUT CHARACTER IN BUFFER
	INC	HL
	LD	(OPOSS),HL		;UPDATE POINTER
	LD	DE,OBUFF+512		;AT END OF BUFFER?
	XOR	A			;CLEAR CARRY
	SBC	HL,DE
	JR	Z,PUTCH0		;WRITE OUT DATA IF END OF BUFFER
PUTRET:	POP	HL
	POP	DE
	POP	BC
	POP	PSW
	RET
PUTCH0:	LD	C,4			;LOOP COUNTER
	LD	HL,OBUFF		;ADDRESS OF DATA
	LD	DE,128			;LENGTH OF EACH RECORD
PUTCH1:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	D,H			;SET UP DMA ADDRESS
	LD	E,L
	LD	C,SETDMA
	CALL	BDOS
	LD	DE,OFCB			;WRITE RECORD TO OUTPUT FILE
OUTPAT	EQU	$-2			;PATCH ADDRESS FOR OUTPUT FILE FCB
	LD	C,WRITE
	CALL	BDOS
	CP	0			;SUCCESS?
	JR	NZ,PUTCH2		;JUMP IF DISK FULL
	POP	HL
	POP	DE
	POP	BC
	ADD	HL,DE			;POINT TO NEXT RECORD
	DEC	C
	JP	NZ,PUTCH1		;LOOP FOR 512 BYTE BUFFER
	LD	HL,OBUFF		;RESET POINTER
	LD	(OPOSS),HL
	CALL	CTRLZ			;FILL BUFFER WITH EOF CHARACTER
	JP	PUTRET			;RETURN
PUTCH2:	LD	C,STROUT		;DISK FULL ERROR
	LD	DE,DSKFUL
	CALL	BDOS
	CALL	BOOT			;GIVE UP
DSKFUL:	DB	CR,LF,'Disk full -- aborting',CR,LF,'$'

;
; CTRLZ -- Fill the output buffer with EOF characters to prepare it for
;	writing

CTRLZ:	LD	HL,OBUFF		;BUFFER ADDRESS
	LD	B,2			;LOOP 256 BYTES 2 TIMES
	LD	C,0
CTRLZ0:	LD	(HL),EOF		;PUT EOF IN BUFFER
	INC	HL
	DEC	C			;FAST COUNTER
	JR	NZ,CTRLZ0
	DEC	B			;SLOW COUNTER
	JR	NZ,CTRLZ0
	RET

;
; MEMWRD -- put word in WORD into memory.  If word already exists in memory
;	then its the address of its status byte is returned in DE.  If the
;	word is not found, the word is placed in memory and the pointers
;	that alphabetize the words are updated.  If memory is full, a
;	1 is returned in A.  Otherwise 0 is returned in A.

MEMWRD:	LD	BC,(SRTBOT)		;GET ADDRESS OF BOTTOM OF WORD POINTER
	LD	DE,(SRTTOP)		;TOP OF WORD POINTER
	JR	MEMSKP
MEMWR0:	POP	HL
	POP	DE
	POP	BC
	JP	M,MEMW00
	LD	D,H			;MOVE HIGH POINTER DOWN
	LD	E,L
	JR	MEMSKP
MEMW00:	LD	B,H			;MOVE LOW POINTER UP
	LD	C,L
MEMSKP:	LD	H,D			;HL IS POINTER TO RECORD BETWEEN DE AND
	LD	L,E			;BC
	XOR	A			;CLEAR CARRY
	SBC	HL,BC
	LD	A,0			;IS BC+1=HL?
	CP	H
	JP	NZ,MEMWR1
	LD	A,2
	CP	L
	JP	NZ,MEMWR1
	LD	HL,(SRTTOP)		;UPDATE SRTTOP=SRTTOP+2
	INC	HL
	INC	HL
	LD	(SRTTOP),HL
	XOR	A			;CLEAR CARRY
	PUSH	HL
	SBC	HL,DE			;NUMBER OF BYTES IN TABLE TO MOVE
	LD	B,H			;PUT NUMBER OF COUNTER FOR LDDR
	LD	C,L
	POP	HL
	PUSH	DE			;SAVE THE ADDRESS FOR NEW WORD
	LD	D,H			;PUT DESTINATION ADDRESS IN DE
	LD	E,L
	DEC	HL			;SOURCE IN HL
	DEC	HL
	LDDR				;PUT A SPACE FOR NEW POINTER IN TABLE
	POP	DE
	LD	HL,(FREE)		;ADDRESS TO STORE NEW WORD
	EX	DE,HL
	LD	(HL),E			;STORE ADDRESS OF WORD IN TABLE
	INC	HL
	LD	(HL),D
	LD	HL,WORD
	LD	A,(LASTC)		;GET NUMBER OF CHARACTERS
	LD	B,A
	INC	B			;B = NUMBER OF CHARACTERS IN WORD + 1
	INC	B
MEMW01:	LD	A,(HL)			;GET A CHARACTER
	LD	(DE),A			;PUT IT IN MEMORY
	DEC	DE			;STORE WORD BACKWARDS
	INC	HL			;NEXT CHAR IN WORD
	DJNZ	MEMW01	
	LD	(FREE),DE		;UPDATE FREE MEMORY POINTER
	EX	DE,HL
	LD	DE,45			;ALLOW ROOM FOR ANOTHER WORD
	XOR	A			;CLEAR CARRY
	SBC	HL,DE
	LD	DE,(SRTTOP)		;POINTER TO TOP OF POINTER TABLE
	XOR	A
	SBC	HL,DE			;IS ANY MEMORY LEFT?
	JR	C,MEMFUL		;IF NOT, NO MORE MEMORY
	LD	A,0			;INDICATE MEMORY LEFT
	RET
MEMFUL:	LD	A,1			;NO MEMORY LEFT
	RET
MEMWR1:	RRC	L			;DIVIDE HL BY 2
	LD	A,L
	AND	07EH			;MAKE IT EVEN
	LD	L,A
	LD	A,0
	RRC	H
	ADC	A,0			;IF BIT 0 OF H SET, THEN C SET
	RRC	A
	OR	L
	LD	L,A
	LD	A,H
	AND	07FH
	LD	H,A
	ADD	HL,BC			;HL POINTS TO RECORD BETWEEN BC AND
					;DE
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	E,(HL)			;DE= ADDRESS OF COMPARISON WORD IN BUFF
	INC	HL
	LD	D,(HL)
	LD	HL,WORD			;HL= ADDRESS OF NEW WORD BUFFER
MEMWR2:	LD	A,(DE)			;GET A CHARACTER
	AND	07FH			;KILL CARRY (FOR 0 BYTE OF CORRECTED
					;WORDS)
	CP	(HL)			;COMPARE THEM
	JP	NZ,MEMWR0		;IF .NE. THEN TRY ANOTHER WORD
	LD	A,0
	CP	(HL)			;END OF WORD?
	DEC	DE			;DECREMENT WORD TABLE POINTER (WORDS
					;ARE STORED BACKWARDS
	INC	HL			;INCREMENT POINTER (DON'T AFFECT
					;FLAGS)
	JR	NZ,MEMWR2
	POP	HL
	POP	BC			;TRASH OLD DE
	POP	BC
	INC	DE			;POINT TO 0 OR 080H AT END OF WORD
					; (80H IS CORRECTED AND WORD FOUND)
	RET				;DON'T BOTHER TO BUFFER THE SAME WORD

;
; AUXDIC -- Open SPELL.DIC and load it into memory.  Open FILE.UDC and load
;	it as well.  If FILE.ADD exists (wordstar dictionary addition file),
;	load it and put its contents in FILE.UDC.

AUXDIC:	LD	HL,NEXT			;ZERO THE MEMORY DICTIONARY
	LD	A,0
	LD	(HL),A

AUXDI2:	LD	DE,FILD$$		;MAKE TEMPORARY .DIC OUTPUT FILE
	LD	C,DELETE
	CALL	BDOS
	LD	DE,FILD$$
	LD	C,MAKE
	CALL	BDOS
	LD	HL,FILD$$		;PATCH THIS FCB INTO OUTPUT ROUTINE
	LD	(OUTPAT),HL
	LD	A,(SAVPUT)		;MAKE PUTCHR: WRITE TO A FILE
	LD	(PUTCHR),A
	LD	DE,FILDIC		;TRY TO OPEN FILE.UDC
	LD	C,OPEN
	CALL	BDOS
	CP	0FFH			;FOUND?
	JP	Z,AUXDI4		;JUMP IF NOT
	LD	HL,FILDIC		;PATCH GETCHR: TO READ FROM THIS FILE
	LD	(FILPAT),HL
	LD	HL,INBUF+512		;PATCH INPTR TO READ ON FIRST CALL
	LD	(INPTR),HL
AUXDI3:	CALL	GETWRD			;GET A WORD INTO SRCWRD:
	JP	Z,AUXDI4		;IF EOF THEN START CHECKING
	CALL	CREWRD			;CREATE WORD FROM SRCWRD:
	LD	A,0			;IS IT AT LEAST 2 CHARS LONG?
	CP	C
	JR	Z,AUXDI3		;FORGET IT IF NOT
	LD	A,41			;IF LONGER THAN 40, ALSO FORGET IT
	CP	C
	JR	Z,AUXDI3
	PUSH	HL
	LD	HL,(FILUDC)		;INCREMENT COUNTER FOR WORDS OUTPUT
	INC	HL			;  TO FILE.UDC
	LD	(FILUDC),HL
	POP	HL
	CALL	SAVWRD			;PUT WORD IN MEMORY
	LD	HL,WORD			;PUT WORD IN OUTPUT FILE
AUXDH3:	LD	A,(HL)			;GET A CHARACTER
	CP	0			;END OF WORD?
	JR	Z,AUXDI3		;QUIT OUTPUT IF END
	CALL	PUTCHR			;OUTPUT CHARACTER
	INC	HL
	JR	AUXDH3

AUXDI4:	LD	DE,FILADD		;TRY TO OPEN FILE.ADD
	LD	C,OPEN
	CALL	BDOS
	CP	0FFH			;FOUND?
	JP	Z,AUXDI7		;RETURN IF NOT
	LD	HL,FILADD		;PATCH GETCHR: TO READ FROM FILE.ADD
	LD	(FILPAT),HL
	LD	HL,INBUF+512		;SET INPTR TO READ ON FIRST CALL
	LD	(INPTR),HL
	LD	A,CR			;PUT CR LF IN OUTPUT FILE
	CALL	PUTCHR
	LD	A,LF
	CALL	PUTCHR
AUXDI5:	CALL	GETCHR			;GET A CHARACTER FROM FILE.ADD
					;IGNORE ALL WORD TYPES EXCEPT 0FFH
					;(EOF)
	CP	0FFH			;END OF WORDS IN CURRENT RECORD?
	JR	Z,AUXDI5		;IF YES, LOOP THROUGH RECORD
	CP	0			;ZERO USED AS FILLER CHARACTER?
	JP	Z,AUXDI5
	CP	EOF			;EOF?
	JP	Z,AUXDI7
	CALL	GETCHR			;THIS CHARACTER IS LENGTH OF WORD
	CP	0			;NO CHARACTERS?
	JR	Z,AUXDI5
	LD	B,A
	LD	HL,WORD			;BUFFER IT IN WORD
AUXDI6:	CALL	GETCHR			;GET A CHARACTER
	LD	(HL),A			;SAVE IT IN WORD:
	INC	HL
	DJNZ	AUXDI6			;GET WHOLE WORD
	LD	A,0			;MARK END OF WORD
	LD	(HL),A
	CALL	SAVWRD			;PUT WORD IN MEMORY
	JP	Z,AUXDI5		;IF WORD ALREADY IN MEMORY THEN DON'T
					;OUTPUT TO FILE.UDC
	PUSH	HL
	LD	HL,(FILUDC)		;INCREMENT COUNTER FOR WORDS OUTPUT
	INC	HL			;  TO FILE.UDC
	LD	(FILUDC),HL
	POP	HL
	LD	HL,WORD			;OUTPUT WORD
AUXDH6:	LD	A,(HL)			;GET A CHARACTER
	INC	HL
	CP	0			;END?
	JR	Z,AUXDK6
	CALL	PUTCHR
	JR	AUXDH6
AUXDK6:	LD	A,CR			;PUT CR LF IN OUTPUT FILE
	CALL	PUTCHR
	LD	A,LF
	CALL	PUTCHR
	JP	AUXDI5			;GET ANOTHER CHARACTER

AUXDI7:	LD	HL,OBUFF		;WRITE OUT REMAINING DATA IN BUFFER
	LD	DE,128
AUXDI8:	LD	A,EOF			;DONE?
	CP	(HL)
	JR	Z,AUXDJ0
	PUSH	DE
	PUSH	HL
	LD	D,H			;SET UP DMA ADDRESS
	LD	E,L
	LD	C,SETDMA
	CALL	BDOS
	LD	DE,FILD$$		;WRITE 128 BYTES TO FILE
	LD	C,WRITE
	CALL	BDOS
	POP	HL
	POP	DE
	CP	0			;ERROR?
	JR	NZ,AUXDI9		;DISK FULL --> BRANCH
	ADD	HL,DE			;POINT TO NEXT RECORD
	JR	AUXDI8
AUXDI9:	LD	C,STROUT		;DISK FULL
	LD	DE,DSKFUL		;DISK FULL MESSAGE (SEE PUTCHR)
	CALL	BDOS
	CALL	BOOT

AUXDJ0:	CALL	RDICT			;READ SPELL.DIC
	LD	HL,BOOT+81H		;POINT TO INPUT LINE BUFFER
	LD	A,(BOOT+80H)		;NUMBER OF CHARACTERS
	LD	B,A
	LD	A,' '			;LOOK FOR A SPACE TO INDICATE END OF
					;INPUT FILE NAME
AUXDJ1:	CP	(HL)
	JR	Z,AUXDJ2		;EXIT LOOP IF SPACE
	INC	HL			;NEXT CHARACTER
	DJNZ	AUXDJ1			;LOOP WHILE CHARACTERS LEFT
	JR	AUXRET			;NOTHING AFTER FILE.  JUST RETURN.
AUXDJ2:	LD	A,'$'			;NOW LOOK FOR $ TO INDICATE MORE DICTS.
AUXDJ3:	CP	(HL)
	JR	Z,AUXDJ4		;EXIT LOOP IF FOUND
	INC	HL
	DJNZ	AUXDJ3			;CONTINUE WHILE CHARACTERS LEFT
	JR	AUXRET			;NOT FOUND
AUXDJ4:	LD	A,' '			;ELMINATE SPACES
	INC	HL			;SKIP '$'
	DJNZ	AUXDJ5			;CONTINUE IF CHAR LEFT
	JR	AUXRET
AUXDJ5:	CP	(HL)
	JR	NZ,AUXDJ6		;STOP IF NOT SPACE
	INC	HL
	DJNZ	AUXDJ5
	JR	AUXRET			;DONE IF JUST SPACES
AUXDJ6:	LD	A,0
	LD	(SPLDIC),A		;USE DEFAULT DRIVE
	LD	DE,SPLDIC+1		;FCB BUFFER TO USE
	LD	A,' '			;NULL FILE NAME (SPACES)
	PUSH	BC
	LD	B,8			;FIRST DO FILENAME, NOT EXTENSION
AUXDJ7:	LD	(DE),A
	INC	DE			;CLEAR ENTIRE FILENAME
	DJNZ	AUXDJ7
	LD	DE,SPLDIC+12		;GET REST OF FCB
	LD	B,24
	LD	A,0			;ZERO REST OF FCB
AUXDJ8:	LD	(DE),A
	INC	DE
	DJNZ	AUXDJ8
	POP	BC
	LD	DE,SPLDIC+1		;POINT TO FCB
	LD	C,0			;NO CHARACTERS WRITTEN TO FCB YET
	LD	A,2			;ARE AT LEAST 3 CHAR LEFT?
	CP	B
	JP	NC,AUXDJ9		;IF NOT, CAN'T BE A DRIVE SPECIFIED
	INC	HL
	LD	A,(HL)			;SEE IF NEXT CHAR IS A ':'
	DEC	HL
	CP	':'
	JR	NZ,AUXDJ9		;JUMP IF NO DRIVE SPECIFIED
	LD	A,(HL)			;GET THE DRIVE
	INC	HL			;POINT TO NEXT CHAR AFTER :
	INC	HL
	DEC	B
	DEC	B
	AND	11011111B		;MAKE SURE IT'S UPPER CASE
	SUB	'A'-1			;MAKE DRIVE NUMBER BASED AT 0
	LD	(SPLDIC),A		;STORE IT IN FCB
AUXDJ9:	LD	A,(HL)			;GET A CHARACTER
	CP	' '			;END OF FILENAME?
	JR	Z,AUXDK2		;READ THE FILE
	CP	'.'			;PERIOD?  DISCARD EXTENSION IF YES.
	JR	Z,AUXDK0
	LD	(DE),A			;FCB IT
	INC	DE
	INC	C
	LD	A,8			;8 CHARACTERS WRITTEN YET?
	CP	C
	JR	Z,AUXDK0
	INC	HL
	DJNZ	AUXDJ9
	JR	AUXDK2			;READ THE FILE
AUXDK0:	LD	A,' '			;SEARCH FOR SPACE TO END FILENAME
					;DISCARD EXTRA CHARACTERS
AUXDK1:	CP	(HL)
	JR	Z,AUXDK2
	INC	HL			;TRY NEXT CHAR
	DJNZ	AUXDK1
AUXDK2:	DEC	HL			;POINT TO CHAR BEFORE END OR SPACE
	INC	B			;1 MORE LEFT NOW
	CALL	RDICT			;READ FROM THE DICTIONARY
	JP	AUXDJ4			;TRY FOR ANOTHER DICTIONARY

AUXRET:	LD	DE,FILD$$		;CLOSE TEMPORARY FILE
	LD	C,CLOSE
	CALL	BDOS
	LD	HL,OFCB			;PATCH PUTCHR: TO OUTPUT TO FILE.$$$
	LD	(OUTPAT),HL
	LD	A,0C9H			;RETURN OPCODE
	LD	(PUTCHR),A		;MAKE PUTCHR: DO NOT OUTPUT
	CALL	CTRLZ			;CLEAR OUTPUT BUFFER
	LD	HL,OBUFF		;CLEAR OUTPUT BUFFER POSITION
	LD	(OPOSS),HL
	LD	HL,(FREE)		;GET ADDRESS OF NEXT FREE BYTE
	INC	HL
	LD	(SRTBOT),HL		;POINT TO TABLE FOR INPUT FILE INDEX
	INC	HL
	INC	HL
	LD	(SRTTOP),HL		;TOP OF TABLE
	LD	HL,(BDOS+1)		;GET ADDRESS OF BDOS
	DEC	HL			;POINT TO FREE MEMORY
	LD	(FREE),HL
	LD	HL,INBUF+512
	LD	(INPTR),HL		;SET INPUT FILE POINTER TO READ RECORD
					;ON NEXT GETCHR: CALL
	CALL	CTRLZ			;CLEAN OUTPUT FILE BUFFER
	LD	A,LF			;RESET LASTCH FOR INPUT
	LD	(LASTCH),A
	LD	DE,UDCMSG		;OUTPUT: "Words read from...FILE.UDC"
	LD	C,STROUT
	CALL	BDOS
	LD	HL,(FILUDC)		;NUMBER OF WORDS WRITTEN
	LD	B,H			;GOES IN BC
	LD	C,L
	LD	HL,FILD$$		;FCB ADDRESS IN HL
	LD	A,'U'			;CHANGE 'FILENAME.D$$' TO '.UDC'
	LD	(FILD$$+9),A
	LD	A,'D'
	LD	(FILD$$+10),A
	LD	A,'C'
	LD	(FILD$$+11),A
	CALL	TYPFIL			;WRITE FILENAME AND # TO CONSOLE
	LD	A,'D'			;CHANGE IT BACK TO '.D$$'
	LD	(FILD$$+9),A
	LD	A,'$'
	LD	(FILD$$+10),A
	LD	(FILD$$+11),A
	RET
UDCMSG:	DB	CR,LF,'Words written to dictionary $'

;
; RDICT - Read dictionary in SPLDIC FCB
;
RDICT:	PUSH	PSW
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,INBUF+512
	LD	(INPTR),HL		;SET INPUT FILE POINTER TO READ RECORD
					;ON NEXT GETCHR CALL
	LD	HL,0
	LD	(SPLDC),HL		;RESET # WORDS READ
	LD	DE,SPLDIC		;FCB FOR SPELL.DIC
	LD	C,OPEN
	CALL	BDOS
	PUSH	PSW
	LD	C,CURDSK		;GET THE DEFAULT DRIVE BEFORE ITS
	CALL	BDOS			;	TOO LATE
	INC	A
	LD	(DEFDRV),A
	POP	PSW
	CP	0FFH			;FOUND?
	JR	NZ,RDICT0		;YES, READ FROM IT
	LD	A,(SPLDIC)		;WAS DEFAULT DRIVE TESTED?
	CP	0
	JR	NZ,RDICT2		;IF SPECIFIC DRIVE TESTED AND NOT
					;FOUND THEN GIVE UP
	LD	A,1			;TRY DRIVE "A"
	LD	(SPLDIC),A
	LD	DE,SPLDIC
	LD	C,OPEN
	CALL	BDOS
	CP	0FFH			;FOUND?
	JP	Z,RDICT2		;IF NOT, GIVE UP
RDICT0:	LD	HL,SPLDIC		;PATCH FCB ADDRESS INTO GETWRD
	LD	(FILPAT),HL
RDICT1:	CALL	GETWRD			;GET A WORD INTO SRCWRD:
	JP	Z,RDICT3		;IF EOF THEN START CHECKING
	CALL	CREWRD			;CREATE WORD FROM SRCWRD:
	LD	A,0			;IS IT AT LEAST 2 CHARS LONG?
	CP	C
	JR	Z,RDICT1		;FORGET IT IF NOT
	LD	A,41			;IF LONGER THAN 40, ALSO FORGET IT
	CP	C
	JR	Z,RDICT1
	PUSH	HL
	LD	HL,(SPLDC)		;INCREMENT COUNTER FOR WORDS IN
	INC	HL			;   SPELL.DIC
	LD	(SPLDC),HL
	POP	HL
	CALL	SAVWRD			;PUT WORD IN MEMORY
	JR	RDICT1

RDICT2:	LD	DE,RNT			;PRINT CR,LF
	LD	C,STROUT
	CALL	BDOS
	LD	A,(SPLDIC)			;DEFAULT DRIVE USED?
	CP	0
	JR	NZ,RDICU0		;JUMP IF NOT
	LD	A,(DEFDRV)		;GET DEFAULT DRIVE
	LD	(SPLDIC),A		;AND PUT IT IN FCB
RDICU0:	LD	A,(SPLDIC)		;GET DRIVE
	ADD	A,'A'-1			;MAKE IT A LETTER
	LD	C,CONOUT
	LD	E,A
	CALL	BDOS			;PRINT DRIVE NAME
	LD	E,':'
	LD	C,CONOUT
	CALL	BDOS			;AND A COLON
	LD	DE,SPLDIC+1		;POINT TO FILENAME
	LD	HL,SPLDIC+12		;POINT TO FIRST BYTE AFTER NAME
	LD	A,'$'
	LD	(SPLDIC+12),A		;MARK END FOR OUPTUT
	LD	C,STROUT
	CALL	BDOS			;PRINT THE FILE NAME

	LD	DE,RNT0			;PRINT NOT FOUND
	LD	C,STROUT
	CALL	BDOS
	JR	RDICTR
RNT:	DB	CR,LF,'$'
RNT0:	DB	' not found$'

RDICT3:	LD	DE,RDIWRD		;FOUND MESSAGE
	LD	C,STROUT
	CALL	BDOS
	LD	HL,(SPLDC)		;STORE # WORDS READ IN BC
	LD	B,H
	LD	C,L
	LD	HL,SPLDIC		;FCB ADDRESS
	CALL	TYPFIL			;TYPE FILE NAME AND # WORDS IN IT
	JR	RDICTR
RDIWRD:	DB	CR,LF,'Words read from dictionary  $'

RDICTR:	POP	HL
	POP	DE
	POP	BC
	POP	PSW
	RET

;
; TYPFIL -- print name of dictionary file being read and words found in it
;
;	Input:  BC -->  Number of words found in file
;		HL -->  Address of file's FCB
;
TYPFIL:	PUSH	PSW
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	A,(HL)			;DEFAULT DRIVE USED?
	CP	0
	JR	NZ,TYPFI0		;JUMP IF NOT
	LD	A,(DEFDRV)		;GET DEFAULT DRIVE
	LD	(HL),A			;AND PUT IT IN FCB
TYPFI0:	LD	A,(HL)			;GET DRIVE
	ADD	A,'A'-1			;MAKE IT A LETTER
	PUSH	BC
	PUSH	HL
	LD	C,CONOUT
	LD	E,A
	CALL	BDOS			;PRINT DRIVE NAME
	LD	E,':'
	LD	C,CONOUT
	CALL	BDOS			;AND A COLON
	POP	HL
	LD	D,H
	LD	E,L
	INC	DE			;POINT TO FILENAME
	LD	BC,12
	ADD	HL,BC			;HL POINTS TO END OF FILENAME
	LD	A,'$'
	LD	(HL),A			;MARK END FOR OUPTUT
	LD	C,STROUT
	CALL	BDOS			;PRINT THE FILE NAME
	LD	DE,TYPSPA		;PRINT SPACES AFTER NAME
	LD	C,STROUT
	CALL	BDOS
	POP	BC
	LD	H,B			;NUMBER OF WORDS FOUND
	LD	L,C
	CALL	DECOUT			;PRINT NUMBER
	POP	HL
	POP	DE
	POP	BC
	POP	PSW
	RET
TYPSPA:	DB	':    $'

;
; SAVWRD -- put WORD in memory dictionary
;

SAVWRD:	CALL	USRTST			;WAS WORD ALREADY MARKED?
	CP	1
	RET	Z			;RETURN WITH Z SET IF ALREADY MARKED
	LD	HL,(FREE)		;GET NEXT AVAILABLE BYTE
	LD	DE,WORD			;POINT TO WORD
SAVWR0:	LD	A,(DE)			;GET A CHARACTER
	LD	(HL),A			;STORE IT IN BUFFER
	INC	DE			;INCREMENT POINTERS
	INC	HL
	CP	0			;END OF WORD?
	JR	NZ,SAVWR0
	LD	(HL),A			;MARK END OF TABLE WITH 0
	LD	(FREE),HL		;UPDATE FREE MEMORY POINTER
	LD	A,1
	AND	A			;MAKE FOR Z NOT SET
	RET

;
; WRDTST -- Search for WORD in dictionary.  If it is not found, try
;	stripping of suffixes and looking for new word with a flag set.
;
;		Returns in A:  0 <-- WORD not found
;			       1 <-- WORD found
;			       2 <-- root word found but necessary
;				     suffix flag not set.  Returned
;				     only if suffixes were stripped.

WRDTST:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,0
	LD	(FLAG),HL		;NO FLAGS FOR FIRST LOOKUP
	CALL	FINDIT			;LOOK FOR WORD IN DICTIONARY
	PUSH	PSW
	LD	A,1			;ASSUME THAT WORD WAS FOUND AND
					;INDICATE WORKING IN ALPHABETICAL ORDER
					;STILL (DECODING OF CURRENT RECORD
					;BY LOOKUP ROUTINE CAN CONTINUE FROM
					;CURRENT POSITION)
	LD	(ALPHA),A
	POP	PSW
	CP	0			;NOT FOUND?
	JP	Z,WRDTS0		;KEEP TRYING IF NOT FOUND
WRDRET:	POP	HL
	POP	DE
	POP	BC
	RET

WRDTS0:	CALL	USRTST			;TEST USER DICTIONARY
	CP	1			;FOUND?
	JP	Z,WRDRET
	LD	A,0			;NOT WORKING IN ALPHABETICAL ORDER
					;ANY MORE
	LD	(ALPHA),A
	LD	HL,LASTC		;PUT NUMBER OF CHARACTERS -1 IN C
	LD	C,(HL)
	LD	B,0			;MAKE BC AN OFFSET TO LAST CHAR
	LD	HL,WORD			;POINT TO WORD
	ADD	HL,BC			;POINT TO LAST CHARACTER IN WORD
	LD	A,(HL)
	QISIT	'E',FLGV		;WORD ENDS IN "E"
	QISIT	'H',FLGH		;"H"
	QISIT	'Y',FLGY		;"LY"
	QISIT	'G',FLGG		;"ING"
	QISIT	'N',FLGN		;"TION, "EN"
	QISIT	'D',FLGD		;"ED", "IED"
	QISIT	'T',FLGT		;"EST", "IEST"
	QISIT	'R',FLGR		;"ER", "IER"
	QISIT	'S',FLGS		;LOTS OF WORDS ENDING IN "S"
	LD	A,0
	JP	WRDRET			;NO FLAGS FIT

FLGV:	LD	A,2			;WORD MUST BE 4 CHARS LONG
	CP	C
	JP	P,WRDNOT		;NOT FOUND IF TOO SHORT
	LD	DE,VFLAG		;LOOKING FOR V FLAG
	LD	(FLAG),DE
	DEC	HL			;CHARACTER BEFORE
	ISIT	'V'
	DEC	HL
	ISIT	'I'
	LD	(HL),'E'		;GET "CREATIVE"
	INC	HL
	LD	(HL),0
	CALL	FINDIT
	CP	0			;FOUND?
	JP	NZ,WRDRET		;BRANCH IF FOUND
	DEC	HL			;POINT TO CHAR BEFORE NEW "E"
	DEC	HL
	JISIT	'E',WRDNOT		;KILL "CREATEIVE"
	INC	HL
	LD	(HL),0
	CALL	FINDIT
	JP	WRDRET

FLGH:	LD	A,2			;MUST BE 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,HFLAG		;SEEKING WORD WITH H FLAG SET
	LD	(FLAG),DE
	DEC	HL
	ISIT	'T'
	DEC	HL
	JISIT	'Y',WRDNOT		;KILL "TWENTYTH"
	INC	HL
	LD	(HL),0			;NEW END OF WORD
	CALL	FINDIT			;GET "HUNDREDTH"
	CP	0			;FOUND?
	JP	NZ,WRDRET		;RETURN IS FOUND
	LD	A,4			;WORDS WITH "IETH" MUST BE 6 CHARS LONG
	CP	C
	JP	P,WRDNOT
	DEC	HL
	ISIT	'E'
	DEC	HL
	ISIT	'I'
	LD	(HL),'Y'		;MODIFY WORD TO END IN "Y"
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "TWENTIETH"
	JP	WRDRET

FLGY:	LD	A,2			;MUST BE 4 CHARACTERS LONG (AT LEAST)
	CP	C
	JP	P,WRDNOT
	LD	DE,YFLAG		;WORDS MUST HAVE Y FLAG SET
	LD	(FLAG),DE
	DEC	HL
	ISIT	'L'
	LD	(HL),0			;MARK NEW END OF WORD
	CALL	FINDIT			;GET "QUICKLY"
	JP	WRDRET

FLGG:	LD	A,2			;MUST BE AT LEAST 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,GFLAG		;SET G FLAG
	LD	(FLAG),DE
FLGGE:	DEC	HL
	ISIT	'N'
	DEC	HL
	ISIT	'I'
	LD	(HL),'E'
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "FILING"
	CP	0
	JP	NZ,WRDRET		;RETURN IF FOUND
	DEC	HL
	DEC	HL
	JISIT	'E',WRDNOT		;KILL "FILEING"
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CROSSING"
	JP	WRDRET

FLGN:	LD	A,2			;MUST BE 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,NFLAG		;SET N FLAG
	LD	(FLAG),DE
FLGNEE:	DEC	HL
	JISIT	'O',FLGNO		;WORD WITH "TION"
	ISIT	'E'			;IF .EQ. "TION" THEN .EQ. "EN"
	DEC	HL
	JISIT	'E',WRDNOT		;KILL "CREATEEN"
	JISIT	'Y',WRDNOT		;KILL "MULTIPLYEN"
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "FALLEN"
	JP	WRDRET
FLGNO:	LD	A,3			;MUST BE 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	DEC	HL
	ISIT	'I'
	LD	(HL),'E'
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CREATION"
	CP	0
	JP	NZ,WRDRET		;RETURN IF FOUND
	LD	A,7			;MUST BE AT LEAST 9 CHARS LONG
	CP	C
	JP	P,WRDNOT
	DEC	HL
	DEC	HL
	ISIT	'T'
	DEC	HL
	ISIT	'A'
	DEC	HL
	ISIT	'C'
	DEC	HL
	ISIT	'I'
	LD	(HL),'Y'
	INC	HL
	LD	(HL),0
	CALL	FINDIT
	JP	WRDRET

FLGD:	LD	A,2			;MUST BE 4 CHARACTERS LONG
	CP	C
	JP	P,WRDRET
	LD	DE,DFLAG		;SET D FLAG
	LD	(FLAG),DE
FLGDE:	DEC	HL
	ISIT	'E'
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CREATED"
	CP	0
	JP	NZ,WRDRET
	DEC	HL
	DEC	HL
	JISIT	'E',WRDNOT		;KILL "CREATEED"
	LD	A,(HL)
	CP	'Y'			;IF .NE. "Y" TRY OTHER SUFFIXES
	JP	NZ,FLGD5
	DEC	HL
	CALL	VOWEL			;VOWEL MUST BE BEFORE "Y"
	JP	NZ,WRDNOT
	INC	HL
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CONVEYED"
	JP	WRDRET
FLGD5:	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CROSSED"
	CP	0
	JP	NZ,WRDRET		;RETURN IF FOUND
	DEC	HL
	ISIT	'I'
	DEC	HL
	CALL	VOWEL			;CAN'T BE A VOWEL
	JP	Z,WRDNOT
	INC	HL
	LD	(HL),'Y'
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "IMPLIED"
	JP	WRDRET

FLGT:	LD	A,2			;MUST BE AT LEAST 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,TFLAG		;T FLAG MUST BE SET
	LD	(FLAG),DE
	DEC	HL
	JISIT	'S',FLGDE		;SAME RULES AS 'D' FLAG IF ENDS
					;IN "ST"
	JP	WRDNOT			;NOT FOUND IF NOT "ST"

FLGR:	LD	A,2			;MUST BE AT LEAST 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,RFLAG		;SET R FLAG
	LD	(FLAG),DE
FLGRE:	JP	FLGDE			;SAME RULES AS D FLAG

FLGS:	LD	A,2			;MUST BE 4 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,SFLAG		;TRY PURE "S" FLAG FIRST
	LD	(FLAG),DE
	DEC	HL
	JISIT	'S',FLGP		;"NESS", "INESS"
	JISIT	27H,FLGM		;"'S"
	CALL	SXZH			;IS IT S, X, Z OR H?
	JP	Z,WRDNOT		;IF YES, THEN ILLEGAL WORD
	LD	A,(HL)			;PURE "S" LEGAL IF .NE. "Y"
	CP	'Y'
	JR	NZ,FLGS2
	DEC	HL
	CALL	VOWEL
	JP	NZ,WRDNOT		;ILLEGAL WORD IF NOT A VOWEL
	INC	HL
FLGS2:	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CONVEYS", "BATS"
	CP	0
	JP	NZ,WRDRET		;RETURN IF FOUND
	DEC	HL
	LD	A,(HL)
	QISIT	'R',FLGZ		;"ERS", "IERS"
	QISIT	'N',FLGX		;"IONS", "ICATIONS", "ENS"
	QISIT	'G',FLGJ		;"INGS"
	ISIT	'E'
	DEC	HL
	CALL	SXZH
	JP	NZ,FLGS5		;IF LETTER NOT S,X,Z OR H TRY "IES"
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "FIXES"
	JP	WRDRET
FLGS5:	ISIT	'I'
	DEC	HL
	CALL	VOWEL
	JP	Z,WRDNOT		;CAN'T BE A VOWEL
	INC	HL
	LD	(HL),'Y'
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "IMPLIES"
	JP	WRDRET

FLGX:	LD	DE,XFLAG		;SET X FLAG
	LD	(FLAG),DE
	JP	FLGNEE			;SAVE AS "N" FLAG NOW

FLGJ:	LD	DE,JFLAG		;SET J FLAG
	LD	(FLAG),DE
	JP	FLGGE			;SAVE AS "G" FLAG NOW

FLGZ:	LD	DE,ZFLAG		;SET Z FLAG
	LD	(FLAG),DE
	JP	FLGRE			;SAVE AS "R" FLAG NOW

FLGP:	LD	A,3			;MUST BE 5 CHARS LONG
	CP	C
	JP	P,WRDNOT
	LD	DE,PFLAG		;SET P FLAG
	LD	(FLAG),DE
	DEC	HL
	ISIT	'E'
	DEC	HL
	ISIT	'N'
	DEC	HL
	LD	A,(HL)
	CP	'Y'
	JP	NZ,FLGP4		;LEGAL IF .NE. "Y"
	DEC	HL
	CALL	VOWEL
	JP	NZ,WRDNOT		;ILLEGAL IF "Y" AND NO VOWEL
	INC	HL
FLGP4:	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "LATENESS", "GRAYNESS"
	CP	0
	JP	NZ,WRDRET		;RETURN IF FOUND
	DEC	HL
	ISIT	'I'
	DEC	HL
	CALL	VOWEL
	JP	Z,WRDNOT		;CAN'T BE A VOWEL
	INC	HL
	LD	(HL),'Y'
	INC	HL
	LD	(HL),0
	CALL	FINDIT			;GET "CLOUDINESS"
	JP	WRDRET

FLGM:	LD	DE,MFLAG		;SET M FLAG
	LD	(FLAG),DE
	LD	(HL),0
	CALL	FINDIT			;GET "DOG'S"
	JP	WRDRET

WRDNOT:	LD	A,0			;NOT FOUND
	JP	WRDRET
;
; VOWEL -- determine whether character in (HL) is a vowel.
;
;		Returns:  Z flag SET <-- is a vowel
;			  Z flag CLEAR <-- is not a vowel

VOWEL:	PUSH	BC
	PUSH	HL
	LD	A,(HL)			;GET CHARACTER
	LD	HL,VOWELS		;POINTER TO VOWELS
	LD	BC,5			;5 VOWELS
	CPIR				;TEST AGAINST ALL VOWELS
					;SET STATUS BITS TO BE USED AFTER RET
	POP	HL
	POP	BC
	RET
VOWELS:	DB	'AEIOU'

;
; SXZH -- same as VOWEL put for the characters S, X, Z and H

SXZH:	PUSH	BC
	PUSH	HL
	LD	A,(HL)			;GET CHARACTER
	LD	HL,SXZH0		;POINTER TO CHARACTER LIST
	LD	BC,4			;4 POTENTIAL MATCHES
	CPIR				;TEST AGAINST S, X, Z, H
	POP	HL
	POP	BC
	RET
SXZH0:	DB	'SXZH'

;
; Determine which record of dictionary would contain WORD.  Puts record in
;	BC

FINDIT:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	A,(ALPHA)		;STILL IN ALPHABETICAL ORDER?
	AND	A
	JP	NZ,FINLO5		;DON'T DECREMENT POINTERS IF YES

	LD	HL,(LSTADR)		;GET ADDRESS OF LAST DICTIONARY POINTER
					;USED
FINLOW:	LD	DE,WORD
	EX	DE,HL
	LD	BC,4
FINLO1:	LD	A,(DE)			;MAKE SURE THAT CURRENT RECORD IS
					;BEFORE WORD
	INC	DE
	CPI
	JP	M,FINLO5		;IF EARLIER, JUMP
	JR	NZ,FINLO2		;IF LATER, THEN DECREMENT
	JP	PE,FINLO1		;LOOP WHILE BC-1 .NE. 0
FINLO2:	LD	BC,(LSTREC)		;DECREMENT THE POINTERS
	DEC	BC
	LD	HL,0			;IS IT TOO LOW?
	XOR	A
	SBC	HL,BC
	JR	Z,FINLO5		;IF SO, THEN FORGET IT
	LD	(LSTREC),BC
	LD	HL,(LSTADR)
	LD	DE,4
	XOR	A			;CLEAR CARRY
	SBC	HL,DE
	LD	(LSTADR),HL
	JP	FINLOW			;TRY AGAIN

FINLO5:	LD	BC,(LSTREC)		;GET LAST RECORD NUMBER READ
	LD	HL,(LSTADR)		;GET ADDRESS OF LAST DICTIONARY POINTER
					;USED
FINDI0:	PUSH	BC
	PUSH	HL
	LD	DE,WORD
	EX	DE,HL
	LD	BC,4			;COMPARING UP TO 4 CHARACTERS
FINDI1:	LD	A,(DE)			;GET A CHARACTER FROM POINTER TABLE
	INC	DE
	CPI				;COMPARE TO WORD:
	JR	NZ,FINDI2		;JUMP IF DIFFERENT
	JP	PO,FINDI3		;JUMP IF ALL 4 CHARACTERS EQUAL
	JR	FINDI1			;TRY ANOTHER
FINDI2:	JP	P,FINDI3		;TOO FAR IN DICTIONARY
	LD	DE,4
	POP	HL
	POP	BC
	ADD	HL,DE			;POINT TO NEXT RECORD INDEX
	INC	BC
	JP	FINDI0
FINDI3:	POP	HL
	POP	BC
FINDI4:	DEC	BC
	LD	DE,4
	XOR	A			;CLEAR CARRY
	SBC	HL,DE
	LD	(LSTREC),BC		;UPDATE POINTERS
	LD	(LSTADR),HL

;
; LOOKUP -- loop through as many records as it takes to be sure that word is
;	not in dictionary

LOOKUP:	CALL	DICFND
	CP	0FFH			;IF RETURN STATUS=0FFH THEN WORD COULD
					;BE IN NEXT RECORD
	JR	NZ,LOOKU0		;RETURN IF NOT IN NEXT RECORD
	INC	BC
	LD	HL,0+(TABBOT-TABTOP)/4	;MAKE SURE NOT PAST LAST RECORD
	XOR	A			;CLEAR THE CARRY BIT
	SBC	HL,BC
	JR	NZ,LOOKUP		;TRY NEXT RECORD IF NOT AT END
	LD	A,0
LOOKU0:	POP	HL			;RESTORE THE STACK
	POP	DE
	POP	BC
	RET

;
; DICFND  -- read record in BC from dictionary.  Determine whether WORD is
;	in it.
;
;		Returns in A:  0 <-- word not found
;			       1 <-- word found
;			       2 <-- word found but flag not set
;			     0FFH<-- word not found but it may be in next
;				     record

DICFND:	PUSH	HL
	PUSH	DE
	PUSH	BC
	RLC	B			;MULTIPLY BC BY 2 SO IT POINTS TO
					;256 BYTE RECORD
	RLC	C
	LD	A,0
	ADC	A,B			;GET THE CARRY FROM RLC C
	LD	B,A
	LD	A,C
	AND	0FEH			;KILL BIT 0
	LD	C,A
	LD	HL,(DICREC)		;GET THE CURRENT RECORD IN RAM
	DEC	HL			;POINT TO FIRST 128 BYTES OF 256
					;BYTE DICTIONARY RECORDS
	XOR	A			;CLEAR CARRY BIT
	SBC	HL,BC			;ATTEMPT TO READ THE SAME RECORD?
	JR	NZ,DICDSK		;IF NO, READ FROM DISK
	LD	A,(ALPHA)		;WORKING IN APHABETICAL ORDER?
	AND	A
	JP	NZ,DICFO1		;JUST GET ANOTHER WORD IF YES
	LD	A,(CURBIT)		;ROTATE CURRENT BYTE SO IT IS THE RIGHT
					;POSITION IN CASE SAME RECORD USED
	AND	0111B			;IS IT ON CORRECT ROTATION NOW?
	JR	Z,DICFN0
	LD	HL,(CURBYT)		;ADDRESS OF CURRENT BYTE
	LD	B,A
	LD	A,8			;MUST COMPLETE 8 ROTATIONS, TOTAL
	SUB	B			;SUBTRACT NUMBER ALREADY DONE
	LD	B,A
ROTATE:	RLC	(HL)
	DJNZ	ROTATE
	JP	DICFN0			;START TESTING WORDS
DICDSK:	PUSH	BC
	LD	(DICREC),BC		;NEW RECORD TO READ
	LD	C,SETDMA		;SET DMA TO DICTIONARY BUFFER
	LD	DE,DICBUF		;DICIONARY BUFFER IN RAM
	CALL	BDOS
	LD	C,RANREA		;BDOS RANDOM READ CODE
	LD	DE,DICFCB		;FCB OF DICTIONARY
	CALL	BDOS
	POP	BC
	INC	BC			;GET 256 BYTES
	LD	(DICREC),BC		;NEXT RECORD NUMBER
	LD	C,SETDMA		;DMA ADDRESS 256 BYTES HIGHER
	LD	DE,DICBUF+128
	CALL	BDOS
	LD	C,RANREA
	LD	DE,DICFCB
	CALL	BDOS			;READ NEXT RECORD

DICFN0:	LD	HL,DICBUF-1		;INITIALIZE POINTER TO DICTIONARY
					;BUFFER
	LD	(CURBYT),HL
	LD	A,0			;CURRENT BIT=0
	LD	(CURBIT),A
DICFN1:	CALL	MOVWRD			;GET A WORD
DICFO1:	LD	HL,WORD			;POINT TO WORD
	LD	BC,DICWRD		;POINTER TO DICTIONARY WORD
DICFN2:	LD	A,(BC)			;GET A LETTER FROM DICTIONARY WORD
	CP	(HL)			;THE SAME?
	INC	HL			;POINT TO NEXT CHARACTER
	INC	BC
	JR	NZ,DICFN4		;IF NOT, TRY SOME MORE OR END
	CP	0			;END OF WORD?
	JR	NZ,DICFN2		;TRY ANOTHER CHARACTER
	LD	C,1			;INDICATE WORD FOUND
	LD	HL,(DICFL)
	LD	A,(FLAG)		;GET FIRST FLAG
	CP	0			;NO FLAG WANTED?
	JR	Z,DICFN3		;TRY NEXT BYTE OF FLAG IF NONE
	AND	L
	CP	0			;INDICATE WORD FOUND IF .NE. 0
	JR	NZ,DICRET
	LD	C,2			;INDICATE MAIN WORD FOUND/NO FLAG
	JR	DICRET
DICFN3:	LD	A,(FLAG+1)		;TRY NEXT FLAG BYTE
	CP	0			;NO FLAG WANTED?
	JR	Z,DICRET		;IF NONE WANTED THEN WORD FOUND
	AND	H
	CP	0			;FOUND IF .NE. 0
	JR	NZ,DICRET
	LD	C,2			;INDICATE MAIN WORD FOUND/NO FLAG
	JR	DICRET

DICFN4:	JP	M,DICFN1		;IF TEST WORD IS EARLIER IN ALPHABET,
					;TRY NEXT DICTIONARY WORD
	LD	C,0			;NOT FOUND
;	JP	DICRET

DICRET:	LD	A,C			;PUT STATUS BYTE IN A
	POP	BC
	POP	DE
	POP	HL
	RET

;
; MOVWRD -- read a word from the dictionary by decoding the flags.
;
;		Returns:  DICWRD <-- word from dictionary

MOVWRD:	LD	A,0
	LD	B,4
	CALL	GETBIT			;GET 4 BITS INTO A
	LD	HL,DICWRD		;POINT TO DICTIONARY WORD BUFFER
	LD	B,0
	LD	C,A			;NUMBER OF CHARACTERS TO KEEP
	ADD	HL,BC
MOVWR1:	LD	A,0
	LD	B,3			;GET FIRST 3 OF 5 BITS FOR CHARACTER
	CALL	GETBIT			;BITS IN A
	CP	0111B			;IS IT AN END OF WORD MARK?
	JR	Z,GETFLG		;GET THE FLAGS NOW
	LD	B,2			;GET REMAINING 2 BITS
	CALL	GETBIT
	CP	0			;IF ZERO THEN END OF RECORD
	JP	Z,MOVMOR		;INDICATE MAY BE IN NEXT RECORD
	ADD	A,40H			;MAKE IT ASCII
	CP	'Z'+1			;IS IT AN ENCODED "'"?
	JR	NZ,MOVWR4		;JUMP IF NOT
	LD	A,027H			;MAKE IT A "'"
MOVWR4:	LD	(HL),A			;BUFFER THE CHARACTER
	INC	HL
	JR	MOVWR1			;GET ANOTHER CHARACTER
MOVMOR:	LD	C,0FFH			;INDICATE MAY BE IN NEXT RECORD
	POP	DE			;GET USELESS RETURN WORD
	JP	DICRET			;RETURN

;
; GETFLG -- read suffix flags from buffer.
;
;		Returns:  DICFL <-- 16 byte flag word

GETFLG:	LD	A,0
	LD	(HL),A			;MARK THE END OF THE WORD
	LD	B,4			;GET 4 BITS FOR NUMBER OF FLAGS VALUE
	CALL	GETBIT
	LD	B,A			;NUMBER OF BITS IN B
	LD	HL,0
	CP	0			;ARE ANY BITS THERE TO COPY?
	JP	Z,GETFL8		;RETURN IF NONE
	PUSH	BC
	CP	8			;MORE THAN 8 BITS?
	JP	M,GETFL1		;JUMP IF NOT
	LD	B,7			;GET 7 BITS FOR FIRST BYTE
GETFL1:	LD	A,0
	CALL	GETBIT
	POP	BC
	LD	L,A			;THIS IS THE LOW BYTE OF FLAGS
	LD	A,B
	CP	8			;GET MORE IF GREATER THAN 8 BITS
	JP	P,GETFL4
	LD	A,7			;COMPUTE NUMBER OF ROTATIONS NECESSARY
					;TO PUT IT IN THE RIGHT PLACE
	SUB	B
	JP	Z,GETFL8		;IF EXACTLY 7 THEN DONE
	LD	B,A			;COUNTER
GETFL3:	RLC	L
	DJNZ	GETFL3
	JP	GETFL8			;NO RETURN
GETFL4:	SUB	7			;GET NUMBER OF BITS NEEDED FOR BYTE 2
	LD	B,A
	LD	A,0
	PUSH	BC
	CALL	GETBIT			;GET BIT FOR HIGH BYTE OF STATUS FLAG
	POP	BC
	LD	H,A			;SAVE HIGH BYTE
	LD	A,7			;COMPUTE NUMBER OF ROTATIONS LEFT
	SUB	B
	JR	Z,GETFL8		;RETURN IF NONE
	LD	B,A
GETFL6:	RLC	H
	DJNZ	GETFL6
GETFL8:	LD	(DICFL),HL		;SAVE THE FLAG
	RET

;
; GETBIT -- read number of bits in B from dictionary buffer.
;
;		Returns:  A <-- byte value of B bits

GETBIT:	PUSH	DE
	PUSH	HL
	EX	AF,AF'			;A HOLDS DESIRED DECODED BYTE.
					;A' HOLD CURRENT BIT VALUE

	LD	A,(CURBIT)
	LD	HL,(CURBYT)		;ADDRESS OF CURRENT BYTE FOR OUTPUT
	LD	D,(HL)			;D = CURRENT BYTE VALUE
	EX	AF,AF'
GETBI0:	EX	AF,AF'			;GET CURRENT BIT VALUE
	AND	0111B			;MASK 1ST THREE BITS
	JR	NZ,GETBI1		;IF .NE. 0 THEN NOT TIME TO INC CURBYT
	LD	(HL),D			;RESTORE OLD BYTE TO ORIGINAL VALUE
	INC	HL
	LD	(CURBYT),HL
	LD	D,(HL)			;UPDATE D = CURRENT BYTE VALUE
GETBI1:	INC	A
	EX	AF,AF'			;BACK TO DECODED BYTE
	RLC	D			;MOVE BYTE SO NEXT BIT IN RIGHT PLACE
	RLC	A			;MAKE A READY TO RECEIVE
	BIT	0,D			;IS THE BIT ON?
	JR	Z,GETBI2		;DON'T SET A IF IT ISN'T
	OR	1			;SET BIT 1 OF A
GETBI2:	DJNZ	GETBI0			;LOOP THROUGH DESIRED NUMBER OF BITS
	EX	AF,AF'			;GET CURRENT BIT
	LD	(CURBIT),A		;UPDATE IT
	EX	AF,AF'
	LD	(HL),D			;UPDATE BYTE FOR NEXT CALL
	POP	HL
	POP	DE
	RET

;
; USRTST -- test user dictionary in memory for WORD.
;
;		Returns in A:  0 <-- word not found
;			       1 <-- word found

USRTST:	LD	HL,NEXT			;BEGINNING OF USER DICTIONARY BUFFER
	LD	A,0
	CP	(HL)			;DOES ONE EXIST?
	RET	Z			;CAN'T FIND ELEMENT OF AN EMPTY SET!
	LD	DE,WORD			;ADDRESS OF WORD FOR COMPARING
USRTS1:	LD	A,(DE)			;GET A CHARACTER
	CP	(HL)			;ARE THEY THE SAME?
	JR	NZ,USRTS2		;IF NOT SAME, TRY ANOTHER WORD
	INC	DE
	INC	HL
	CP	0			;END OF WORD?
	JR	NZ,USRTS1		;LOOP THROUGH WHOLE WORD
	LD	A,1			;FOUND IT!
	RET
USRTS2:	LD	A,0
USRTS3:	CP	(HL)			;LOOK FOR END OF WORD
	INC	HL
	JR	NZ,USRTS3
	CP	(HL)			;IF NEXT BYTE ALSO 0 THEN END OF TABLE
	RET	Z
	LD	DE,WORD
	JR	USRTS1			;TEST ANOTHER WORD

;
;  DECOUT - Output number in HL to console in decimal
;

DECOUT:	PUSH	PSW
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	B,0			;B WILL BE 1 ONCE NON-ZERO CHAR OUTPUT
	LD	DE,10000		;START BY TRYING 10,000'S
	CALL	NUMOUT			;OUTPUT A NUMBER
	LD	DE,1000			;1000'S
	CALL	NUMOUT
	LD	DE,100
	CALL	NUMOUT
	LD	DE,10
	CALL	NUMOUT
	LD	B,1			;GUARANTEE THAT 0 WILL PRINT
	LD	DE,1
	CALL	NUMOUT
	POP	HL
	POP	DE
	POP	BC
	POP	PSW
	RET

NUMOUT:	LD	C,0			;COUNTER FOR NUMBER OF SUBTRACTIONS
NUMOU0:	INC	C			;COUNT LOOPS THROUGH SUBTRACTION
	XOR	A			;CLEAR CARRY
	SBC	HL,DE			;SUBTRACT UNITS UNTIL CARRY
	JP	NC,NUMOU0
	ADD	HL,DE			;RESET TO LAST POSITIVE VALUE
	DEC	C			;DON'T COUNT LAST SUBRTRACTION
	JR	NZ,NUMOU1		;IF NOT ZERO, THEN OUTPUT
	CP	B			;ANYTHING OUTPUT YET?
	RET	Z			;IF NOT, THEN DON'T PRINT A 0
NUMOU1:	LD	B,1			;INDICATE OUTPUT SENT
	LD	A,C
	ADD	A,'0'			;CONVERT TO ASCII
	LD	E,A			;OUTPUT TO CONSOLE
	LD	C,CONOUT		;CONSOLE OUTPUT CODE
	PUSH	BC
	PUSH	HL
	CALL	BDOS
	POP	HL
	POP	BC
	RET



INPTR:	DW	INBUF+512		;POINTER TO CURRENT BYTE IN INPUT BUFF
OPOSS:	DW	OBUFF			;POINTER TO CURRENT BYTE IN OUTPUT BUFF
SRTBOT:	DW	NEXT			;POINTER TO BEGINNING OF MEMORY POINTER
					;TABLE OF ALPHABETIZED WORDS FROM INPUT
					;FILE
SRTTOP:	DW	NEXT+2			;POINTER TO TOP OF MEMORY POINTER TABLE
FREE:	DW	NEXT			;POINTER TO NEXT FREE BYTE IN TPA
LASTCH:	DB	LF			;LAST CHARACTER INDICATOR
OLSTCH:	DB	LF			;BUFFER FOR OLD LAST CHARACTER WHEN
					;MULTIPLE INPUT FCB'S IN USE
SAVPUT:	DS	1			;BUFFER FOR NORMAL FIRST INSTRUCTION
					;AT PUTCHR.  USED WHILE OUTPUT IS
					;DISABLED
DICFL:	DW	0			;DICTIONARY FLAG FOR COMPARE
CURBYT:	DW	DICBUF-1		;CURRENT BYTE OF DICBUF (FOR GETBIT)
CURBIT:	DB	0			;CURRENT BIT OF BYTE (FOR GETBIT)
ALPHA:	DB	0			;0 IF NOT WORKING IN ALPHABETICAL
					;ORDER (DID FLAG SEEK).  1 IF IN ORDER
LSTREC:	DW	1			;RECORD NUMBER OF LAST DICTIONARY
					;RECORD READ
LSTADR:	DW	TABTOP+4		;POINTER TO TABLE WHERE FIRST 4 BYTES
					;OF LAST RECORD READ ARE FOUND
OLDPTR:	DW	0			;POINTER FOR PASS 1 OF INPUT FILE IS
					;SAVED HERE DURING PASS 2 IF ALL OF
					;FILE DID NOT FIT IN MEMORY
P2OPTR:	DW	P2BUF+512		;POINTER FOR PASS2 OF INPUT FILE IS
					;SAVED HERE DURING PASS 1 IF ALL OF
					;FILE DID NOT FIT IN MEMORY
STOPED:	DB	0			;IS 1 IF ALL OF FILE DID NOT FIT IN
					;MEMORY
MLTPAS:	DB	0			;SAME AS STOPED BUT NEVER RESET ONCE
					;SET
DEFDRV:	DB	0			;RECEIVES DEFAULT DRIVE VALUE
SPLDC:	DW	0			;STORES NUMBER OF WORDS IN SPELL.DIC
FILUDC:	DW	0			;STORES NUMBER OF WORDS WRITTEN TO
					;FILE.UDC
TOTWRD:	DW	0			;RECORDS TOTAL NUMBER OF WORDS IN DOC
UNQWRD:	DW	0			;NUMBER OF UNIQUE WORDS IN DOC
MISWRD:	DW	0			;NUMBER OF MISSPELLED WORDS

OFCB:	DB	0,'        $$$'		;FCB FOR OUTPUT FILE
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0
P2FCB:	DB	0,'           '		;FCB FOR PASS 2 OF INPUT FILE
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0
FILDIC:	DB	0,'        UDC'		;FCB FILE FILE.UDC
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0
FILADD:	DB	0,'        ADD'		;FCB FOR FILE.ADD
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0
FILD$$:	DB	0,'        D$$'		;FCB FOR TEMPORARY FILE FILE.D$$
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0
SPLDIC:	DB	0,'SPELL   DIC'		;FCB FOR SPELL.DIC
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0
DICFCB:	DB	0,'DICT    DIC'		;FCB FOR DICTIONARY FILE
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0
DICREC:	DB	0,0,0			;RECORD NUMBER FOR RANDOM READ
					;(STILL PART OF FCB)

STACK	EQU	$+50			;STACK
SRCWRD	EQU	STACK			;BUFFER FOR UNFIXED WORD
WORD	EQU	SRCWRD+100		;BUFFER FOR FIXED WORD
DICWRD	EQU	WORD+41			;BUFFER FOR WORDS READ FROM DICTIONARY
FLAG	EQU	DICWRD+41		;BUFFER FOR DESIRED DICTIONARY FLAG
LASTC	EQU	FLAG+2			;BUFFER FOR LAST CHARACTER READ
DICBUF	EQU	LASTC+1			;BUFFER FOR DICTIONARY RECORD
INBUF	EQU	DICBUF+258		;INPUT BUFFER FOR PASS 1
P2BUF	EQU	INBUF+513		;INPUT BUFFER FOR PASS 2
OBUFF	EQU	P2BUF+513		;OUTPUT BUFFER
NEXT	EQU	OBUFF+513		;ADDRESS OF NEXT FREE BYTE
	END	START
